#include "Mesh.h"
#include "Error.h"
#include "StringUtility.h"
#include "MyGL.h"
#include "AllocationManager.h"
#include "Memory.h"
#include <fstream>

namespace Engine
{	
	typedef std::vector <std::string> Strings;

	void Split(const std::string& cs, char c, Strings* strings)
	{
		std::string s(cs);
		while (true) 
		{
			int i = s.find(c);
			if (i == std::string::npos)
			{
				// No special character found, so push back the entire string and finish.
				strings ->push_back(s);
				break; 
			}    
			else
			{
				// Found the special character. 
				// Push back whatever is before the character, then work on the remainder
				// of the string.
				strings ->push_back(s.substr(0, i));
				s = s.substr(i + 1);
				
				while (!s.empty() && (s[0] == ' ' || s[0] == '\r' || s[0] == '\n' ) )
				{
					s = s.substr(1);
				}
				while (!s.empty() && (s[s.size() - 1] == ' ' || s[s.size() - 1] == '\r' || s[s.size() - 1] == '\n') )
				{
					s = s.substr(0, s.size() - 1);
				}

				if (s.empty())
				{
					break;
				}
			}
		}
	}

	bool Mesh::Load(const std::string& fileName)
	{
		std::ifstream file(fileName);
		if(!file.is_open()) 
		{
			Error("Unable to open mesh " + fileName);
			return false;
		}

		std::string currentLine;
		Strings strings;//used in split func to seperate line into seperate words
		Strings vals;//used to get vals for vecs etc
		Strings indices;//used to get indices for faces
		bool loaded = true;

		while(!file.eof())
		{
			std::getline(file, currentLine);

			// ---> Louie: commented out this line. it's giving me errors.
			//
			//if(currentLine[0] == '#' || currentLine.empty() == true) continue;

			//seperate the current line into seperate words
			strings.clear();
			Split(currentLine, ' ', &strings);
			unsigned int numTs = 0;//tangent & uvs are called t (first t allways uvs)

			if(strings[0] == "mesh")
			{
				int val = 0;

				//going through mesh components sizes
				for(unsigned int n = 2; n < strings.size() - 1; n += 2)
				{
					if(!FromString(val, strings[n + 1]))
					{
						Error("Unable to get mesh component size value for Mesh " + fileName);
						return false;
					}

					if(strings[n] == "v")
						m_posVec.reserve(val * 3);

					else if(strings[n] == "n")
						m_normalVec.reserve(val * 3);

					else if(strings[n] == "t")
					{
						if(numTs == 0) m_uvVec.reserve(val * 2);
						++numTs;
					}

					else if(strings[n] == "f")
						m_faces.reserve(val * 3);
				}
			}
			else if(strings[0] == "msh_v")
			{
				vals.clear();
				Split(strings[1], ',', &vals);

				Vec3 v;
				if(!FromString(v.x, vals[0]))
				{
					loaded = false;
					break;
				}
				if(!FromString(v.y, vals[1]))
				{
					loaded = false;
					break;
				}
				if(!FromString(v.z, vals[2]))
				{
					loaded = false;
					break;
				}

				m_posVec.push_back(v);
			}

			else if(strings[0] == "msh_uv")
			{
				vals.clear();
				Split(strings[1], ',', &vals);

				Vec2 v;
				if(!FromString(v.x, vals[0]))
				{
					loaded = false;
					break;
				}
				if(!FromString(v.y, vals[1]))
				{
					loaded = false;
					break;
				}

				m_uvVec.push_back(v);
			}
	
			else if(strings[0] == "msh_nrml")
			{
				vals.clear();
				Split(strings[1], ',', &vals);

				Vec3 v;
				if(!FromString(v.x, vals[0]))	
				{
					loaded = false;
					break;
				}
				if(!FromString(v.y, vals[1]))
				{
					loaded = false;
					break;
				}
				if(!FromString(v.z, vals[2]))
				{
					loaded = false;
					break;
				}

				m_normalVec.push_back(v);
			}

			else if(strings[0] == "msh_f")
			{
				//Face	
				vals.clear();
				Split(strings[1], ',', &vals);
	
				int val = 0;

				Face f;

				//first vertex indices
				indices.clear();
				Split(vals[0], '/', &indices);
				if(!FromString(val, indices[0]))
				{
					loaded = false;
					break;
				}
				f.verts[0] = val;
				if(!FromString(val, indices[1]))
				{
					loaded = false;
					break;
				}
				f.uvs[0] = val;
				if(!FromString(val, indices[2]))
				{
					loaded = false;
					break;
				}
				f.normals[0] = val;


				//second vertex indices
				indices.clear();
				Split(vals[1], '/', &indices);
				if(!FromString(val, indices[0]))
				{
					loaded = false;
					break;
				}
				f.verts[1] = val;
				if(!FromString(val, indices[1]))
				{
					loaded = false;
					break;
				}
				f.uvs[1] = val;
				if(!FromString(val, indices[2]))
				{
					loaded = false;
					break;
				}
				f.normals[1] = val;
	
				//third vertex indices
				indices.clear();
				Split(vals[2], '/', &indices);
				if(!FromString(val, indices[0]))
				{
					loaded = false;
					break;
				}
				f.verts[2] = val;
				if(!FromString(val, indices[1]))
				{
					loaded = false;
					break;
				}
				f.uvs[2] = val;
				if(!FromString(val, indices[2]))
				{
					loaded = false;
					break;
				}
				f.normals[2] = val;

				m_faces.push_back(f);
			}
		}

		#ifdef MEM_DEBUG

		int size = (m_posVec.size() * sizeof Vec3);
		size += (m_normalVec.size() * sizeof Vec3);
		size += (m_uvVec.size() * sizeof Vec2);
		size += (m_faces.size() * sizeof Face);

			TheAllocationManager::Instance() ->AddAllocation(Allocation(&this, size,__FILE__, __LINE__));
		#endif //MEM_DEBUG

		//this is added here because some data may be pushed into the vectors even if an error occurs
		if(!loaded)
		{
			Error("Error reading Model File " + fileName);
			return false;
		}

		//name of asset needs to be set
		m_name = fileName;
		return true;
	}

	bool Mesh::Load(rapidxml::xml_node <>* node)
	{
		if(!node ->first_node("FileName"))
		{
			Error("Unable to find Mesh FilePath in xml file");
			return false;
		}
	
		return Load(node ->first_node("FileName") ->value());
	}
	
	void Mesh::Draw() 
	{
		glBegin(GL_TRIANGLES);
			for(unsigned int n = 0; n < m_faces.size(); ++n)
				for(unsigned int i = 0; i < 3; ++i)
				{
					glNormal3fv(m_normalVec[m_faces[n].normals[i]].ToArray());
					glTexCoord2fv(m_uvVec[m_faces[n].uvs[i]].ToArray());
					glVertex3fv(m_posVec[m_faces[n].verts[i]].ToArray());
				}
		glEnd();
	}

	Mesh::~Mesh()
	{
		#ifdef MEM_DEBUG
			//even if a mesh was not successfully loaded, something might have been pushed back into vecs
			if(m_posVec.size() || m_normalVec.size() || m_uvVec.size() || m_faces.size())
				TheAllocationManager::Instance() ->RemoveAllocation(&this);
		#endif //MEM_DEBUG
	}
}
                  